/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.composite.helper;

import java.util.ArrayList;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Collaboration;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.ConnectableElement;
import org.eclipse.uml2.uml.Connector;
import org.eclipse.uml2.uml.ConnectorEnd;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.StructuredClassifier;

import com.cea.papyrus.core.helper.GraphEdgeHelper;
import com.cea.papyrus.core.helper.IGraphEdgeHelper;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.ui.dialogs.PapyrusMessageDialog;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.core.utils.VectorCalculus;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Factory that can create and delete a {@link org.eclipse.uml2.uml.Connector}.
 */
public class EdgeConnectorHelper extends GraphEdgeHelper implements IGraphEdgeHelper{

	/////////////////////////////////////////////////////////////////////////////////
	//                       GRAPHICAL MANAGEMENT
	/////////////////////////////////////////////////////////////////////////////////

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#canExecute(com.cea.papyrus.diagraminterchange2.di2.GraphNode)
	 */
	/**
	 * 
	 * 
	 * @param parentGE 
	 * 
	 * @return 
	 */
	@Override
	public boolean canCreateGraphElement(GraphElement parentGE){
		Element eltSource=null;
		if (parentGE.getSemanticModel()!=null ){
			if (((Uml1SemanticModelBridge)parentGE.getSemanticModel()).getElement()!=null){
				eltSource=((Uml1SemanticModelBridge)parentGE.getSemanticModel()).getElement();
				// source uml element should be a connectable element
				if (eltSource instanceof  StructuredClassifier) { // source is a connectable element
					return true;
				}
			}
		}
		return false;
	}
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#createGraphEdge(org.eclipse.uml2.uml.Element, com.cea.papyrus.diagraminterchange2.di2.Diagram)
	 */
	/**
	 * 
	 * 
	 * @param umlElement 
	 * @param diagram 
	 * 
	 * @return 
	 */
	@Override
	public ArrayList createGraphEdge(Element umlElement, Diagram diagram){
		Debug.debug(this,"createGraphEdge", 10);
		GraphEdge ge=(GraphEdge)this.createGraphElement(umlElement, null, null, null);

		//look for UML connected
		if (!(umlElement instanceof Connector)){
			Debug.error(this, "EdgeConnectorFactory.createGraphEdge: "+ umlElement+ "is not a dependency!", channel);
		}
		Connector connector=(Connector)umlElement;
		Element umlSource = ((ConnectorEnd)connector.getEnds().get(0)).getRole();
		Element umlTarget = ((ConnectorEnd)connector.getEnds().get(1)).getRole();
		super.endCreateGraphElement(umlSource, umlTarget, ge, diagram);
		return new ArrayList();
	}
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.NodeFactory#createGraphNodeWithSemantic(org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param umlChild 
	 * @param size 
	 * @param location 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public GraphElement createGraphElement(Element umlChild, GraphElement parent, Point location, Dimension size) {
		GraphElement graphElement=super.createGraphElement(umlChild, null, location, size);
		NodeHelper nf=new NodeHelper();
		
		// name label of the connector
		GraphElement graphNode=nf.createGraphElement(null, graphElement, new Point(0, 10), null);
		graphNode.setFontSize(9);
		graphNode.setIsVisible(true);

		// stereotype label of the connector
		graphNode=nf.createGraphElement(null, graphElement, new Point(0, 30), null);
		graphNode.setFontSize(9);

		//the parent has to be connected in last, because a event will be triggered.
		if(parent!=null) {
			parent.getContained().add(graphElement);
		}
		return graphElement;	
	}

	/////////////////////////////////////////////////////////////////////////////////
	//                       LINK MANAGEMENT
	/////////////////////////////////////////////////////////////////////////////////
	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#connect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param link 
	 * @param source 
	 * 
	 * @return 
	 */
	@Override
	public boolean connect(Element source, Element target, Element link) {
		boolean b = false;

		Debug.debug(this, "Connector.connect() from "+source+" to "+target, channel);

		// retrieves uml element
		Connector connector=(Connector)link;	
		connector.setName(getNewUMLElementName(link.getOwner()));
		ConnectableElement cSource=(ConnectableElement)source;
		ConnectableElement cTarget=(ConnectableElement)target;

		// creates the end
		connector.getEnds().clear();
		// creates connector end and set role for source element
		ConnectorEnd sourceEnd = connector.createEnd();
		sourceEnd.setRole(cSource);
		// creates connector end and set role for target element
		ConnectorEnd targetEnd = connector.createEnd();
		targetEnd.setRole(cTarget);

		// add ends to the set of connector ends
		b = connector.getEnds().add(sourceEnd);
		b = b & connector.getEnds().add(targetEnd);
		return b;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#deconnect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param link 
	 * @param source 
	 * 
	 * @return 
	 */
	@Override
	public boolean deconnect(Element source, Element target, Element link) {
		Connector connector=(Connector)link;
		connector.setName(getNewUMLElementName(link.getOwner()));
		// remove elements from role
		// verify it is not null
		Object tmp  = connector.getEnds().get(0);
		if( tmp != null && tmp instanceof ConnectorEnd) {
			((ConnectorEnd)tmp).setRole(null);
		}
		tmp  = connector.getEnds().get(1);
		if( tmp != null && tmp instanceof ConnectorEnd) {
			((ConnectorEnd)tmp).setRole(null);
		}
		return true;
	}
	

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#canConnect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param source 
	 * 
	 * @return 
	 */
	@Override
	public boolean canConnect(Element source, Element target) {
//		if no source or no target found, can not connect
		if ((source instanceof  org.eclipse.uml2.uml.Port||source instanceof  org.eclipse.uml2.uml.Property)
				&& (target ==null)) { 
			// both elements are ConnectableElements
			return true;
		}
		if ((source == null) && (target == null)) {
			return false;
		}

		// if target is the source, can not connect
		if (source == target) {
			return false;
		}
//		 Test source and target types
		
		// Test source and target types
		if ((source instanceof  org.eclipse.uml2.uml.Port||source instanceof  org.eclipse.uml2.uml.Property)
				&& (target instanceof  org.eclipse.uml2.uml.Port||target instanceof  org.eclipse.uml2.uml.Property)) { 
			// both elements are ConnectableElements
			return true;
		}
		return false;
	}


//	@Override
//	public GraphEdge createGraphEdge(){
//	Debug.debug(this,"Call ModelResourceManager.createConnector", 10);
//	//uml element
//	Connector co= UMLFactory.eINSTANCE.createConnector();
//	co.createEnd();
//	co.createEnd();
//	GraphEdge ge=this.createGraphEdgeWithSemantic(co);
//	return ge;	
//	}

//	@Override
//	public boolean canExecute(GraphElement source,GraphElement target){
//	Element eltSource = null;
//	Element eltTarget = null;

//	if ((source.getSemanticModel() == null) || (target.getSemanticModel() == null)) {
//	return false;
//	}

//	eltSource=((Uml1SemanticModelBridge)source.getSemanticModel()).getElement();
//	eltTarget=((Uml1SemanticModelBridge)target.getSemanticModel()).getElement();

//	// if no source or no target found
//	if ((eltSource == null) || (eltTarget == null)) {
//	return false;
//	}

//	// if target is the source
//	if (eltSource == eltTarget) {
//	return false;
//	}

//	// Test source and target types
//	if ((eltSource instanceof ConnectableElement) && (eltTarget instanceof ConnectableElement))
//	{ // they are  ConnectableElement
//	return true;
//	}

//	return false;
//	}

//	@Override
//	public boolean canExecuteSource(GraphElement source){
//	Element eltSource=null;
//	if (source.getSemanticModel()!=null ){
//	if (((Uml1SemanticModelBridge)source.getSemanticModel()).getElement()!=null){
//	eltSource=((Uml1SemanticModelBridge)source.getSemanticModel()).getElement();
//	if (eltSource instanceof  org.eclipse.uml2.uml.ConnectableElement){// they are  classifiers
//	return true;
//	}

//	}
//	}

//	return false;
//	}

//	@Override
//	public ArrayList createGraphEdge(Element umlElement, Diagram diagram){
//	Debug.debug(this,"createGraphEdge", 10);
//	GraphEdge ge=this.createGraphEdgeWithSemantic(umlElement);
//	//look for UML connected UML element
//	Connector co= (Connector)umlElement;


//	Element umlsupplier=((ConnectorEnd) co.getEnds().get(0)).getRole();
//	Element umlsource=((ConnectorEnd) co.getEnds().get(1)).getRole();
//	super.connect(umlsource, umlsupplier, ge, diagram);
//	return new ArrayList();
//	}

//	@Override
//	public GraphEdge createGraphEdgeWithSemantic(org.eclipse.uml2.uml.Element umlElement) {

//	GraphEdge graphEdge=super.createGraphEdgeWithSemantic(umlElement);
//	NodeFactory nf=new NodeFactory();
////	name of Association
//	GraphNode graphNode=nf.createGraphElement();
//	graphNode.setFontSize(9);
//	graphNode.setPosition(new Point(0, 10));
//	graphNode.setIsVisible(false);
//	graphEdge.getContained().add(graphNode);



////	stereotype of Association
//	graphNode=nf.createGraphElement();
//	graphNode.setFontSize(9);
//	graphNode.setPosition(new Point(0, 30));
//	graphEdge.getContained().add(graphNode);
//	return graphEdge;	
//	}

	/////////////////////////////////////////////////////////////////////////////////
	//                       UMLManagement
	/////////////////////////////////////////////////////////////////////////////////

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#createUMLElement(java.lang.String, org.eclipse.uml2.uml.Element)
	 */
	/**
 * 
 * 
 * @param name 
 * @param parent 
 * 
 * @return 
 */
public  org.eclipse.uml2.uml.Element createUMLElement(String name, org.eclipse.uml2.uml.Element parent){
		Debug.debug(this,"Call create UML element", channel);
		Connector connector= null;
		if( parent instanceof Property){
			 connector=((Class)parent.getOwner()).createOwnedConnector(name);
		} else if(parent instanceof Collaboration){
			connector=((Collaboration)parent).createOwnedConnector(name);
		} else if(parent instanceof Component){
			connector=((Component)parent).createOwnedConnector(name);
		} else if(parent instanceof Class) {
			connector=((Class)parent).createOwnedConnector(name);
		} else throw new RuntimeException("do not know how to create a collaboration using this parent: "+parent.eClass().getInstanceTypeName());
		
		//create 2 ends
		connector.createEnd();
		connector.createEnd();
		return connector;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#getBaseString()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public String getBaseString(){
		return "Connector_";
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#addUMLElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param child 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public boolean addUMLElement(org.eclipse.uml2.uml.Element child, org.eclipse.uml2.uml.Element parent){
		Debug.debug(this,"add UML element", channel);
		
		if(parent instanceof Component) {
			return ((Component)parent).getOwnedConnectors().add((Connector)child);
		}
		if(parent instanceof Class) {
			return ((Class)parent).getOwnedConnectors().add((Connector)child);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#removeUMLElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param child 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public boolean removeUMLElement(org.eclipse.uml2.uml.Element child, org.eclipse.uml2.uml.Element parent){
		Debug.debug(this,"remove UML element", channel);
		
		if(parent instanceof Component) {
			return ((Component)parent).getOwnedConnectors().remove((Connector)child);
		}
		if(parent instanceof Class) {
			return ((Class)parent).getOwnedConnectors().remove(child);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#canCreateUMLElement(org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public boolean canCreateUMLElement(org.eclipse.uml2.uml.Element parent){
		
		if( parent instanceof Component){
			return true;
		}
		if( parent instanceof Class){
			return true;
		}
		return false;
	}
	
	/**
	 * 
	 * 
	 * @param ge 
	 * @param umlsource 
	 * @param diagram 
	 * @param umlTarget 
	 */
	public void endCreateGraphElement(Element umlsource, Element umlTarget, GraphEdge ge, Diagram diagram){
		ArrayList di2suppliers=LookForElement.lookForGraphElementWODiagram(umlTarget, diagram, new ArrayList());
		ArrayList di2sources=LookForElement.lookForGraphElementWODiagram(umlsource, diagram, new ArrayList());

		GraphElement source=null;
		GraphElement target=null;

		
		if(di2sources.size()==0 ||di2suppliers.size()==0){
			PapyrusMessageDialog.display("You have to drop target and source manually before", null);
			return;
		}
		if ((di2sources.size()!=0) && (di2suppliers.size()!=0)){
			source=(GraphNode) di2sources.get(0);
			target=(GraphNode) di2suppliers.get(0);
		}

		//the source does not exists
		/*else if((di2sources.size()==0) && (di2suppliers.size()!=0)){
			//create the source
			Element ei= LookForElement.(((Package)umlTarget.getOwner()), umlsource);
			if(ei==null){
				ei=umlsource;
			}

			if (!(ei instanceof ElementImport)){
				source=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				source=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram, null, null);
			}
			target=(GraphNode) di2suppliers.get(0);
//			position of the target
			if(source instanceof GraphNode){
				source.setPosition(new Point(target.getPosition().x-((GraphNode)source).getSize().width-SPACE_X,target.getPosition().y));
			}

		}
		//the target does not exist
		else if((di2sources.size()!=0) && (di2suppliers.size()==0)){
			//create the supplier
			source = (GraphNode)di2sources.get(0);
			Element ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlTarget);
			if(ei==null){
				ei=umlTarget;
			}

			if (!(ei instanceof ElementImport)){
				target=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				target=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram, null, null);
			}
//			position of the target
			if(target instanceof GraphNode){
				target.setPosition(new Point(source.getPosition().x+((GraphNode)source).getSize().width+SPACE_X,source.getPosition().y));
			}
		}
		else {
//			create all
			ArrayList GraphNodes=null;
			Element ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlsource);
			if(ei==null){
				ei=umlsource;
			}
			if (!(ei instanceof ElementImport)){
				source=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				source=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram, null, null);
			}
			ei=null;
			ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlTarget);
			if(ei==null){
				ei=umlTarget;
			}

			if (!(ei instanceof ElementImport)){
				target=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}
			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				target=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram, null, null);
			}
			//target = (GraphNode)GraphNodes.get(0);

//			position of the target
			if (target instanceof GraphNode){
				target.setPosition(new Point(source.getPosition().x+((GraphNode)source).getSize().width+SPACE_X,source.getPosition().y));
			}
		}*/
		
		
		//all element exist!
		//1. create connector
		GraphConnector sourceConnector=Di2Factory.eINSTANCE.createGraphConnector();
		GraphConnector targetConnector=Di2Factory.eINSTANCE.createGraphConnector();

		//2. calculate position for each element

		//2.1 position of the source connector		
		//Point p=LookForElement.calculateAbsolutePosition((GraphNode)source, new Point(0,0));
		sourceConnector.setPosition(new Point (((GraphNode)source).getPosition().x+((GraphNode)source).getSize().width, ((GraphNode)source).getPosition().y+((GraphNode)source).getSize().height/2));
		//2.2 position if the target Connector
		//p=LookForElement.calculateAbsolutePosition((GraphNode)target, new Point(0,0));
		targetConnector.setPosition(new Point(((GraphNode)target).getPosition().x, ((GraphNode)target).getPosition().y+((GraphNode)target).getSize().height/2));

		//3. Connection of all element
		//3.1 connect anchor to graphEdge
		((GraphEdge)ge).getAnchor().add(SOURCE,sourceConnector);
		((GraphEdge)ge).getAnchor().add(TARGET,targetConnector);
		((GraphConnector)ge.getAnchor().get(SOURCE)).setGraphElement(source);
		((GraphConnector)ge.getAnchor().get(TARGET)).setGraphElement(target);
		if( ((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Association){
			VectorCalculus.setGoodPositionForAssociation(ge,(GraphNode)source,(GraphNode)target);}

		Debug.debug(this,"   target dependency link connected", channel);
		((GraphNode)source.getContainer()).getContained().add(ge);
		
	}
}
